using System;
using System.Collections.Generic;
using System.Text.RegularExpressions;
using SpCop.Core.Model;

namespace SpCop.Model
{
    /// <summary>
    /// Represents a file with mixed content.
    /// </summary>
    public class MixedContentFile
    {
        private static readonly Regex RxComment = new Regex("<(?<quote>[%!]?)--.*?--\\k<quote>?>(\\w*\\r?\\n)?",
                                                            RegexOptions.Singleline | RegexOptions.Compiled);

        private static readonly Regex RxDirective =
            new Regex(
                "(<%@\\s*(?<elementName>\\w+)(\\s+(?<name>\\w+)=(?<quote>[\"']?)(?<value>[^\"']*)\\k<quote>)*\\s*%>(\\w*\\r?\\n)?)",
                RegexOptions.Singleline | RegexOptions.Compiled);

        private static readonly Regex RxScript = new Regex("<%(?![@=\\$])(?<body>.*?)%>(\\w*\\r?\\n)?",
                                                           RegexOptions.Singleline | RegexOptions.Compiled);

        private static readonly Regex RxScript2 =
            new Regex("<%\\s?\\$\\s?Resource:(?<file>.*?,)?(?<body>.*?)%>(\\w*\\r?\\n)?",
                      RegexOptions.Singleline | RegexOptions.Compiled);

        private static readonly Regex RxExpression = new Regex("<%=(?<body>.*?)%>");

        private static readonly Regex PlaceHolderExpression =
            new Regex(
                "<asp:Content\\s*(?<elementName>\\w+)(\\s+(?<name>\\w+)=(?<quote>[\"']?)(?<value>[^\"']*)\\k<quote>)*\\s*\\>(\\w*\\r?\\n)?\\</asp:Content\\>",
                RegexOptions.Compiled);

        private readonly string content;
        private List<ContentPart> parts = new List<ContentPart>();

        /// <summary>
        /// Constructs a MixedContentFile for the given file having given conetent.
        /// </summary>
        public MixedContentFile(string content, int initialContentType)
        {
            // Set properties:
            this.content = content;

            // Seed the parts list:
            parts.Add(new ContentPart(this, initialContentType, 0, content.Length, null));

           
        }

        /// <summary>
        /// Content of the file.
        /// </summary>
        public string Content
        {
            get { return content; }
        }

        /// <summary>
        /// List of parts the file is made of.
        /// </summary>
        public List<ContentPart> Parts
        {
            get { return parts; }
        }

        /// <summary>
        /// Applies a parser regular expression to identifiy types of content.
        /// </summary>
        /// <param name="typeToSearch">The content type to search in.</param>
        /// <param name="typeToSearchFor">The content type of the parts matching the given regular expression.</param>
        /// <param name="expressionToSearchFor">The regular expression to use to match content parts.</param>
        public void ApplyParserRegex(int typeToSearch, int typeToSearchFor, Regex expressionToSearchFor)
        {
            var result = new List<ContentPart>();
            foreach (ContentPart section in parts)
            {
                if (section.Type == (MixedContentTypes) typeToSearch)
                {
                    int pos = 0;
                    foreach (Match match in expressionToSearchFor.Matches(section.Content))
                    {
                        if (pos < match.Index)
                        {
                            result.Add(new ContentPart(section.File, section.Type, section.Offset + pos,
                                                       match.Index - pos, section.Match));
                        }
                        result.Add(new ContentPart(section.File, typeToSearchFor, section.Offset + match.Index,
                                                   match.Length, match));
                        pos = match.Index + match.Length;
                    }
                    if (pos < section.Length)
                    {
                        result.Add(new ContentPart(section.File, section.Type, section.Offset + pos,
                                                   section.Length - pos, section.Match));
                    }
                }
                else
                {
                    result.Add(section);
                }
            }

            // Perform iteration substitution:
            parts = result;
        }


        public static MixedContentFile Parse(string content)
        {
            // Initiate document:
            var file = new MixedContentFile(content, (int) MixedContentTypes.Body);

            // Iteration 1: comments
            file.ApplyParserRegex((int) MixedContentTypes.Body, (int) MixedContentTypes.Comment, RxComment);

            // Iteration 2: directives
            file.ApplyParserRegex((int) MixedContentTypes.Body, (int) MixedContentTypes.Directive, RxDirective);


            // Iteration 5: ContentPlaceHolders
            file.ApplyParserRegex((int) MixedContentTypes.Body, (int) MixedContentTypes.PlaceHolder, PlaceHolderExpression);

            // Iteration 3: scripts
            file.ApplyParserRegex((int) MixedContentTypes.Body, (int) MixedContentTypes.ResourceString, RxScript2);
            file.ApplyParserRegex((int) MixedContentTypes.Body, (int) MixedContentTypes.Script, RxScript);

            // Iteration 4: expressions
            file.ApplyParserRegex((int) MixedContentTypes.Body, (int) MixedContentTypes.Expression, RxExpression);


            // Return file:
            return file;
        }



    }
}